#include "dumpsender.h"
#include "ntdproto.h"

#define TICK_INTERVAL 1//1ms

DumpSender::DumpSender(IConnection::PROTO_TYPE type, const SocketAddress& remote_addr, 
					   size_t pkt_len, uint32 limit)
{
	port_ = NULL;
	if( type == IConnection::PROTO_UDP ){
		port_ = new UDPPort(SocketAddress());
	}else if( type == IConnection::PROTO_TCP ){
		port_ = new TCPPort(SocketAddress());
	}else{
		//PROTO_PSEUDOTCP Not support yet
		assert(false);
	}

	port_->AddConnListener(dynamic_cast<IConnListener*>(this));

	real_conn_ = port_->CreateConnection(remote_addr);
	assert(real_conn_);

	conn_ = new RatelimitConn(real_conn_, false);

	tick_.AddListener(dynamic_cast<INTDTimerListener*>(this));

	if( type == IConnection::PROTO_UDP ){
		cc_monitor_ = new UDPCongestMonitor(real_conn_->GetConvId());
	}else if( type == IConnection::PROTO_TCP ){
		cc_monitor_ = new TCPCongestMonitor(real_conn_->GetConvId());
	}else{
		//PROTO_PSEUDOTCP Not support yet
		assert(false);
	}

	real_conn_->AttachConnMonitor(cc_monitor_);

	dump_data_ = NULL;
    SetPktlen(pkt_len);

	SetRateLimit(UINT_MAX);
}

DumpSender::~DumpSender()
{
	port_->RemoveConnListener(dynamic_cast<IConnListener*>(this));

	tick_.RemoveListener(dynamic_cast<INTDTimerListener*>(this));

	delete real_conn_;
	delete conn_;
}

void DumpSender::SetRateLimit(uint32 limit)
{
	LOG4CPLUS_DEBUG(g_logger, "SetRateLimit, conv_id = "<<conn_->GetConvId()
		<<", ratelimit = "<<limit
		<<", proto = "<< conn_->GetProtoType());
	conn_->SetRateLimit(limit);
}

void DumpSender::SetPktlen(size_t ptk_len)
{
	payload_len_ = ptk_len - IP_HEAD_LEN;

	if( real_conn_->GetProtoType() == IConnection::PROTO_UDP ){
		payload_len_ -= UDP_HEAD_LEN;
	}else if( real_conn_->GetProtoType() == IConnection::PROTO_TCP ){
		payload_len_ -= TCP_HEAD_LEN;
	}else{
		payload_len_ -= UDP_HEAD_LEN;
        //TODO: substract pseudotcp header  
	}

	//Ping is enabled by default, so the header need to substract also
	payload_len_ -= NtdProtoHead::HEAD_LEN;


	if( payload_len_ < 0 ){
		payload_len_ = 1;
	}else if( payload_len_ > g_settings.m_mss ){
		payload_len_ = g_settings.m_mss;
	}

	if( dump_data_ ){
		delete []dump_data_;
	}

	dump_data_ = new byte[payload_len_];
	//memset(dump_data_, 0, payload_len_*sizeof(byte));
}

void DumpSender::OnWritable(IConnection* pConn, bool writable)
{
	//LOG4CPLUS_THIS_TRACE(g_logger, "WRITABLE: "<<writable);
	assert( pConn == dynamic_cast<IConnection*>(real_conn_) );

	if( writable ){
		SendOutDump();
		tick_.Start(TICK_INTERVAL, true);
	}else{
		//tick_.Stop();
	}

	//Message * pMsg = new Message();
	//pMsg->phandler = this;
	//pMsg->pdata = WrapMessageData(writable);
	//pMsg->Post();
}

//void DumpSender::OnMessage(Message *pmsg)
//{
//	bool writable = UseMessageData<bool>(pmsg->pdata);
//	delete pmsg->pdata;
//	if( writable ){
//		tick_.Start(TICK_INTERVAL, true);
//		SendOutDump();
//	}else{
//		tick_.Stop();
//	}
//}

void DumpSender::OnError(IConnection* pConn, int err)
{
	if( real_conn_ != NULL ){
		assert( pConn == dynamic_cast<IConnection*>(real_conn_) );
		Dispose(real_conn_);
		real_conn_->DetachConnMonitor(cc_monitor_);
		real_conn_ = NULL;
		delete cc_monitor_;
		tick_.Stop();
		tick_.RemoveListener(dynamic_cast<INTDTimerListener*>(this));
	}

}

void DumpSender::OnDestroy(IConnection* pConn)
{
	delete conn_;
	conn_ = NULL;

	//real_conn_ will delete automatically, no longer	
	//real_conn_ = NULL;
}

void DumpSender::OnTimer(NTDTimer* pTimer)
{
	SendOutDump();
}

void DumpSender::SendOutDump()
{
	while(true){
		int32 ret = conn_->Send(dump_data_, payload_len_);
		if( ret <= 0 ){
			break;
		}
	}
}